import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import static com.googlecode.javacv.cpp.opencv_core.IplImage;

public class ContentFinder {
    private float _threshold = -1f;
    private CvHistogram _histogram = null;

    /**
     * Set threshold for converting the back-projected image to a binary.
     * If value is negative no thresholding will be done.
     */
    void setThreshold(Float t) {
    	_threshold = t;
    }

    /**
     * Set reference histogram, it will be normalized.
     */
    void setHistogram(CvHistogram h) {
        _histogram = h;
        cvNormalizeHist(h, 1);
    }
    /**
     * back project.
     */
    IplImage find(IplImage image) {
    	// Split the input image into channels, each image passed to `cvCalcBackProject` must be single channel.
        IplImage[] channels = ColorHistogram.splitChannels(image);
        // Convert each channel image to a to 32 bit floating point image.
        for(int i = 0; i < channels.length; i++){
        	channels[i] = toIplImage32F(channels[i]);
        }

        // Back project, the original image and the destination image need to have the same depth and nr of color channels
        IplImage dest = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
        cvCalcBackProject(channels, dest, _histogram);

        //apply threshold to destination image
        if (_threshold > 0)
            cvThreshold(dest, dest, _threshold, 1, CV_THRESH_BINARY);
        
        //cleanup
        for(int i = 0; i < channels.length; i++){
        	cvReleaseImage(channels[i]);
        }
        return toIplImage8U(dest,true);
    }

	private IplImage toIplImage32F(IplImage src){
		IplImage dest = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, src.nChannels());
		cvConvertScale(src, dest, 1, 0);
		//cleanup
		src.release();
		return dest;
	}

	private IplImage toIplImage8U(IplImage src, boolean doScaling){
	    double[] min = {Double.MAX_VALUE};
	    double[] max = {Double.MIN_VALUE};
	    cvMinMaxLoc(src, min, max);
	    double scale, offset;
	    if (doScaling) {
	    	scale = 255 / (max[0] - min[0]);
	    	offset = -min[0];
	    } else {
	        scale = 1d;
	        offset = 0d;
	    }
	
	    IplImage dest = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, src.nChannels());
	    cvConvertScale(src, dest, scale, offset);
	    //cleanup
	    cvReleaseImage(src);
	    return dest;
	}
}
